// use crate::model::Fund;
use anyhow::anyhow;
use anyhow::Ok;
use anyhow::Result;
use surrealdb::opt::PatchOp;
use surrealdb::sql::Id;
use surrealdb::sql::Thing;

use crate::model::history::EntityType;
use crate::model::history::FudaHistory;
use crate::model::FudaRecord;

use super::FUDB;
use super::FUHLC;

///
/// force to create a history_item record, if any exist will be deleted
///
/// @param history_item: history_item information
///
pub async fn upsert(history_item: &FudaHistory) -> Result<Option<FudaRecord>> {
    let mut old_history_item: Option<FudaHistory> = None;
    if let Some(id) = history_item.id.as_ref() {
        println!("\n(history_item::upsert)history_item.id.id.to_raw(): {}", id.id.to_raw());
        old_history_item = FUDB.select(("history", id.id.to_raw())).await?;
    }

    let thing = match &old_history_item {
        Some(FudaHistory { id: Some(t), .. }) => t.clone(),
        _ => {
            let vv = select(
                history_item.entity_type.clone(),
                history_item.entity_id.clone(),
                0,
                1
            ).await?;

            if let Some(FudaHistory { id: Some(t), .. }) = vv.first() {
                println!("(history::upsert) from vv.first() got t: {:?}", t);
                t.clone()
            } else {
                // Directly creating a new Thing with a ULID
                Thing::from(("history", Id::ulid()))
            }
        }
    };

    let mut updated: Option<FudaRecord> = None;
    let mut to_update = false;
    let timestamp = FUHLC.new_timestamp().to_string();
    // Update a record with a specific ID
    let mut patch = FUDB.update(thing).patch(PatchOp::replace("/last_updated", &timestamp));

    match old_history_item {
        Some(FudaHistory { id: Some(t), .. }) => {
            updated = Some(FudaRecord { id: t.into() });
        }
        _ => {
            // 新建，下面这些字段只有在新建时候才会有
            patch = patch.patch(PatchOp::replace("/entity_type", &history_item.entity_type));
            patch = patch.patch(
                PatchOp::replace("/entity_id", match history_item.entity_id.as_ref() {
                    Some(v) => v.as_str(),
                    None => "",
                })
            );
            patch = patch.patch(
                PatchOp::replace("/entity_name", match history_item.entity_name.as_ref() {
                    Some(v) => v.as_str(),
                    None => "",
                })
            );
        }
    }

    patch = patch.patch(PatchOp::replace("/tstamp", &timestamp));
    to_update = true;

    if to_update {
        updated = patch.await.map_err(|err| anyhow!(err))?;
    }

    Ok(updated)
}

///
/// delete history_item by it's id , e.g. SH.STK.600519
///
pub async fn delete(history_item_id: &str) -> Result<FudaHistory> {
    let rs: Option<FudaHistory> = FUDB.delete(("history", history_item_id)).await?;
    match rs {
        Some(history_item) => Ok(history_item),
        None =>
            Err(anyhow!(format!("Failed to delete, History not found for {}", history_item_id))),
    }
}

/// select history_items
///
/// pi: u16, page index, start from 0
///
/// pn: u16, page size,
///
pub async fn select(
    entity_type: Option<EntityType>,
    entity_id: Option<String>,
    pi: u16,
    pn: u16
) -> Result<Vec<FudaHistory>> {
    let q_str_part = match (&entity_type, &entity_id) {
        (None, None) => "",
        (None, Some(_)) => "WHERE entity_id = $entity_id",
        (Some(_), None) => "WHERE entity_type = $entity_type",
        (Some(_), Some(_)) => "WHERE entity_type = $entity_type AND entity_id = $entity_id",
    };

    let start = pi * pn;

    let q_str = &format!(
        "SELECT * FROM history {} ORDER BY tstamp DESC LIMIT {} START {}",
        q_str_part,
        pn,
        start
    );

    // Fetch kline data within the specified date range using bind variables
    let mut query = FUDB.query(q_str);

    if let Some(t_) = &entity_type {
        query = query.bind(("entity_type", t_)); // Bind the start_date variable
    }

    if let Some(t_) = &entity_id {
        query = query.bind(("entity_id", t_)); // Bind the start_date variable
    }

    // println!("\nq_str={} \n\nquery={:?}", q_str, query);

    let mut response = query.await?;

    // println!("\n(history_item::select) response= {:?}\n", response);

    let values: Vec<FudaHistory> = response.take(0)?;

    Ok(values)
}

#[cfg(test)]
mod test {
    use crate::db;
    use crate::db::FUHLC;
    use crate::model;
    use crate::model::history::EntityType;

    #[tokio::test]
    async fn test_upsert_history_item() {
        let _ = db::init().await.unwrap();

        for i in 0..100 {
            let history_item = model::history::FudaHistory {
                id: None,
                entity_type: Some(EntityType::Tbot),
                entity_id: Some(format!("entity_id{}", i)),
                entity_name: Some(format!("entity_name{}", i)),
                tstamp: Some(format!("tstamp{}", i)),
                last_updated: Some(format!("tstamp{}", i)),
            };

            // Update a trade_bot record with a specific id
            let res = db::history::upsert(&history_item).await.unwrap();

            println!("Upserted history item: {:?}", res);
        }
    }

    #[tokio::test]
    async fn test_select_history_items() {
        // generate a timestamp
        let ts = FUHLC.new_timestamp();
        println!("\nexercise ts = {}", ts.to_string());

        let _ = db::init().await.unwrap();

        let result = db::history::select(None, None, 0, 10).await;

        println!("result= {:?}\n", result);
        assert_eq!(result.is_ok(), true);

        if let Ok(values) = &result {
            for (index, value) in values.iter().enumerate() {
                println!("[{:02}]= {:?} ", index, value);
            }
        }
    }
}
